<?php

declare(strict_types=1);
/**
 * This file is part of bearing-web.
 *
 * @link     https://www.bearing.cn/
 * @contact  tech@bearing.cn
 * @license  https://gitee.com/cppo/bearing_web/-/blob/master/README.md
 */

namespace common\services\search;

use common\models\ProductDetailModel;
use common\models\ProductModel;
use yii\base\InvalidConfigException;
use yii\elasticsearch\ActiveRecord;
use yii\elasticsearch\Exception;

class BaseSearchService
{
    /**
     * @var string 搜索模型定义路径
     */
    protected string $modelClass = '';

    /**
     * @var null|ActiveRecord|mixed 搜索模型实例
     */
    protected ?ActiveRecord $model = null;

    /**
     * @var int 文档id
     */
    protected int $searchDocId = 0;

    public function __construct()
    {
        if ($this->modelClass) {
            $this->model = new $this->modelClass();
        }
    }

    public function isValid(): bool
    {
        return $this->modelClass && $this->model instanceof ActiveRecord;
    }

    /**
     * Check Index.
     * @throws InvalidConfigException
     * @throws Exception
     */
    public function checkIndex(): bool
    {
        $db = $this->model::getDb();
        $command = $db->createCommand();
        if (! $command->indexExists($this->model::index())) {
            return false;
        }
        return true;
    }

    /**
     * Create this model's index.
     * @throws Exception
     * @throws InvalidConfigException
     */
    public function createIndex()
    {
        $db = $this->model::getDb();
        $command = $db->createCommand();
        return $command->createIndex($this->model::index(), [
            'mappings' => $this->mapping(),
        ]);
    }

    /**
     * Set (update) mappings for this model.
     * @throws Exception
     * @throws InvalidConfigException
     */
    public function updateMapping(): void
    {
        $db = $this->model::getDb();
        $command = $db->createCommand();
        $command->setMapping($this->model::index(), $this->model::type(), $this->mapping());
    }

    /**
     * Delete this model's index.
     * @throws Exception
     * @throws InvalidConfigException
     */
    public function deleteIndex(): void
    {
        $db = $this->model::getDb();
        $command = $db->createCommand();
        $command->deleteIndex($this->model::index());
    }

    public function getSearchDocId(): int
    {
        return $this->searchDocId;
    }

    /**
     * 更新数据.
     * @throws InvalidConfigException|\yii\db\Exception
     */
    public function updateData(ProductModel $model): bool
    {
        $obj = $this->model::findOne(['id' => $model->id]);
        $keys = $this->model->attributes();
        $attrs = $this->getProductDetail($model);
        if (! $obj instanceof $this->modelClass || ! $obj->create_time) {
            $obj = new $this->modelClass();
            $obj->create_time = time();
        }
        foreach ($keys as $v) {
            if (isset($attrs[$v])) {
                $obj->{$v} = is_array($attrs[$v]) ? $attrs[$v][0]['value'] : $attrs[$v];
            }
        }
        $obj->update_time = time();
        $obj->thumb = $model->thumb;
        $obj->price = $model->price;
        $obj->id = $model->id;
        $obj->name = $model->name;
        return $obj->save();
    }

    /**
     * 获取Elasticsearch索引属性值
     */
    protected function mapping(): array
    {
        return method_exists($this->model, 'mapping') ? $this->model::mapping() : [];
    }

    /**
     * 设置属性值
     * @param array $data 属性对应值
     * @throws InvalidConfigException
     */
    protected function setAttrValue(array $data = []): array
    {
        $item = [];
        $attrs = $this->model->attributes();
        foreach ($attrs as $attr) {
            if (! isset($item[$attr]) && isset($data[$attr])) {
                $item[$attr] = $data[$attr];
            }
        }
        return $item;
    }

    /**
     * 获取商品属性.
     * @throws InvalidConfigException
     */
    protected function getProductDetail(ProductModel $model): array
    {
        $object = ProductDetailModel::findOne(['product_id' => $model->id]);
        return $this->setAttrValue(json_decode($object->attr, true));
    }
}
